/** ****************************************************************** *
 * @author Antoine David - Antoine.David@etu.enseeiht.fr
 * @version 1.0beta (not validated by the client)					   *
 * @date 2/3/11  													   *
 * @brief This file contains the provider for the IPSecPolicy. 		   *
 *																	   *
 * *********************************************************************
 * 
 * This provider is in charge of managing the consistency between 
 * IPSecPolicy (CIM-objects) and IPSec policies configurations.
 *
 * One interesting thing is that it supports standard CIM-operations, 
 * such as create_instance, enumerate_instance, etc.
 *
 * 
 * You'll find more on the technical report. 
 * 
 * 
 * */



#include "IPSecPolicy_Provider.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdlib>


#define TRACEFILE "ippolicy.trace"


CIMPLE_NAMESPACE_BEGIN

std::ofstream TraceFile;
std::ofstream LogFile;

String debug;

IPSecPolicy_Provider::IPSecPolicy_Provider()
{
}

IPSecPolicy_Provider::~IPSecPolicy_Provider()
{
}

Load_Status IPSecPolicy_Provider::load()
{
	
	debug("#1","Loading Provider",NULL);
    return LOAD_OK;
}

Unload_Status IPSecPolicy_Provider::unload()
{
	debug("#2","Unloading Provider",NULL);
    return UNLOAD_OK;
}

Get_Instance_Status IPSecPolicy_Provider::get_instance(
    const IPSecPolicy* model,
    IPSecPolicy*& instance)
{
	debug("#3","Entering Get_Instance",NULL);
	
        for (size_t i = 0; i < _array.size(); i++)
		{
			
			IPSecPolicy* ipsecp = _array[i];
			debug("#4","Enumerate Instance",ipsecp->clone());

        if (key_eq(model,ipsecp))
        {
            instance = ipsecp->clone();
            debug("#5","Instance founded",_array[i]->clone());
            return GET_INSTANCE_OK;
        }
    }
	debug("#6","Leaving get_instance [KO]: Instance Not Found",NULL);
    return GET_INSTANCE_NOT_FOUND;
}

Enum_Instances_Status IPSecPolicy_Provider::enum_instances(
    const IPSecPolicy* model,
    Enum_Instances_Handler<IPSecPolicy>* handler)
{
    
    debug("#7","Entering Enum_Instance",NULL);
     for (size_t i = 0; i < _array.size(); i++)
    {
        IPSecPolicy* ipsecp = _array[i];
        debug("#8","Enumerate Instance",ipsecp->clone());
        handler->handle(ipsecp->clone());
    }
	debug("#9","Leaving Enum_Instance [OK]",NULL);	
    return ENUM_INSTANCES_OK;
}

Create_Instance_Status IPSecPolicy_Provider::create_instance(
    IPSecPolicy* instance)
{
    // try to execute the command
    debug("#10","Entering create_instance",instance->clone());
	int ret = create_cmd("spdadd", instance);
	
	if (ret != 0) { 
		debug("#11","Leaving Create_Instance [KO]: create instance failed",instance->clone());
		return CREATE_INSTANCE_FAILED; 
	}
	
	for (size_t i = 0; i < _array.size(); i++)
    {
        IPSecPolicy* ipsecp = _array[i];
        debug("#12","Enumerate Instance",ipsecp->clone());

        if (key_eq(instance, ipsecp)){
			debug("#13","Leaving create_instance [KO]: duplicate",instance->clone());
            return CREATE_INSTANCE_DUPLICATE;
		}
    }

    _array.append(instance->clone());
	
	debug("#14","Leaving create_instance [OK]",instance->clone());
    return CREATE_INSTANCE_OK;
}

Delete_Instance_Status IPSecPolicy_Provider::delete_instance(
    const IPSecPolicy* instance)
{
	debug("#15","Entering Delete_Instance",NULL);
    for (size_t i = 0; i < _array.size(); i++)
    {
		IPSecPolicy* ipsecp = _array[i];
		debug("#16","Enumerate Instance",_array[i]->clone());

        if (key_eq(instance, ipsecp))
        {
			debug("#17","Instance founded",ipsecp);
			
			int ret = create_cmd("spddelete", ipsecp->clone());
			if (ret != 0) { 
				
				debug("#18","Leaving Delete_instance [KO]",NULL);
				return DELETE_INSTANCE_FAILED; 
			}
			
            _array.remove(i);
            IPSecPolicy::destroy(ipsecp);
            
            debug("#19","Leaving Delete_Instance [OK]",ipsecp->clone());
            return DELETE_INSTANCE_OK;
            
		}
	}
	
	debug("#20","Leaving Delete_Instance [KO]: instance not found",NULL);
	return DELETE_INSTANCE_NOT_FOUND;
}

Modify_Instance_Status IPSecPolicy_Provider::modify_instance(
    const IPSecPolicy* model,
    const IPSecPolicy* instance)
{
	debug("#21","Entering Modify_Instance",NULL);
    for (size_t i = 0; i < _array.size(); i++)
    {
        IPSecPolicy* ipsecp = _array[i];
		debug("#22","Enumerate Instance",_array[i]->clone());
        if (key_eq(instance, ipsecp))
        {
			debug("#23","Instance Founded",ipsecp->clone());
	        IPSecPolicy* ipsecp_new = ipsecp->clone();
        	
        	// We modify the parameters we specified.
        	if (!(instance->action.value).empty())           	ipsecp_new->action.set(instance->action.value);
        	if (!(instance->tunnel_in.value).empty())          	ipsecp_new->tunnel_in.set(instance->tunnel_in.value);
        	if (!(instance->tunnel_out.value).empty())          ipsecp_new->tunnel_out.set(instance->tunnel_out.value);
        	if (!(instance->type.value).empty())          		ipsecp_new->type.set(instance->type.value);
        	if (!(instance->transport_type.value).empty())      ipsecp_new->transport_type.set(instance->transport_type.value);
        	if (!(instance->level.value).empty())          		ipsecp_new->level.set(instance->level.value);
        	
			
  	
        	int ret = create_cmd("spddelete", ipsecp);
			
			// If the execution of the command worked.
			if (ret == 0)
			{
				debug("#24","Delete OK",ipsecp->clone());
				// We delete the old rule from iptables.
				ret = create_cmd("spdadd", ipsecp_new);
				
				// If the execution of the command worked.
		    	if (ret == 0)
				{
					debug("#25","ADD OK",ipsecp_new->clone());
					// Both the addition and deletion worked, we modify the instance.
				    copy(ipsecp, ipsecp_new, model);
				    
				    // The modification worked.
				    debug("#26","Leaving Modify_instance [OK]",NULL);
					return MODIFY_INSTANCE_OK;
				}
				
				// If the addition worked but not the deletion, we do a rollback by deleting the new rule.
				create_cmd("spdadd", ipsecp);
				debug("#27","Leaving Modify-Instance [KO] ADD FAILED",NULL);
				// The modification didn't work.
				return MODIFY_INSTANCE_FAILED;
			}
				
			// The modification didn't work.
			debug("#28","Leaving Modify-Instance [KO] DELETE FAILED",NULL);
			return MODIFY_INSTANCE_FAILED;
        }
    }

	// We didn't found the instance we wanted to modify.
	debug("#29","Leaving Modify-Instance [KO]  Instance not found",NULL);
    return MODIFY_INSTANCE_NOT_FOUND;
}


int IPSecPolicy_Provider::create_cmd(
    const char* action,
    IPSecPolicy* instance)
{
	
			
	TraceFile.open("current.spec");
		
	TraceFile << action << " ";
	TraceFile << (instance->source.value).c_str() ;
	TraceFile << " " 	<< (instance->destination.value).c_str();
	TraceFile << " " 	<< (instance->protocol.value).c_str();
	TraceFile << " -P " << (instance->direction.value).c_str();
	
	if (action == "spdadd")
	{
		TraceFile << " " 	<< (instance->action.value).c_str();
		TraceFile << " "	<<  (instance->type.value).c_str();
		TraceFile << "/"	<<  (instance->transport_type.value).c_str();
		TraceFile << "/";
		if (instance->transport_type.value == "tunnel")
		{	
			TraceFile <<  (instance->tunnel_in.value).c_str();
			TraceFile << "-"	<<  (instance->tunnel_out.value).c_str();	
		}
		
		TraceFile << "/" 	<< (instance->level.value).c_str();
		TraceFile << ";";
		
	}
	else
	{
		TraceFile << ";";
	}
	
	TraceFile.close();
	
	int ret = system("setkey -f current.spec");
	
	
	return ret;
}

void IPSecPolicy_Provider::debug(
    const char* level,
    const char* msg,
    IPSecPolicy* instance)
    {
		std::ifstream fichier("config.txt");
		if (fichier)
		{
			std::string line;
			std::getline(fichier,line);
			
			if (line == "debug=yes")
		{
			LogFile.open(TRACEFILE,std::ios_base::app);
			
			LogFile << level;
			LogFile << " " << msg << "\n";
			if (instance != NULL)
			{
				LogFile << "[";
				LogFile << "source=" << (instance->source.value).c_str();
				LogFile << ",";
				LogFile << "destination=" << (instance->destination.value).c_str() << ",";
				LogFile << "protocol=" << (instance->protocol.value).c_str() << ",";
				LogFile << "direction=" << (instance->direction.value).c_str() << ",";
				LogFile << "action=" << (instance->action.value).c_str() << ",";
				LogFile << "type=" << (instance->type.value).c_str() << ",";
				LogFile << "transport_type=" << (instance->transport_type.value).c_str() << ",";
				LogFile << "tunnel_in=" << (instance->tunnel_in.value).c_str() << ",";
				LogFile << "tunnel_out=" << (instance->tunnel_out.value).c_str() << "," ;
				LogFile << "level=" << (instance->level.value).c_str() ;
				LogFile << "]\n";
				
			}
			LogFile.close();
		}
		}
			
		
	return;	
	}
	



/*@END@*/

CIMPLE_NAMESPACE_END
